package com.jiangyg.mall.product.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.jiangyg.mall.core.exception.BusinessException;
import com.jiangyg.mall.core.support.restful.Pagination;
import com.jiangyg.mall.core.utils.Logger;
import com.jiangyg.mall.core.utils.bean.BeanUtils;
import com.jiangyg.mall.coupon.dto.SkuConcessionDTO;
import com.jiangyg.mall.coupon.dto.SpuBoundsDTO;
import com.jiangyg.mall.coupon.service.SkuConcessionService;
import com.jiangyg.mall.coupon.service.SpuBoundsService;
import com.jiangyg.mall.db.mybatis.BaseServiceImpl;
import com.jiangyg.mall.db.mybatis.metadata.Pager;
import com.jiangyg.mall.product.constant.ColumnConstant;
import com.jiangyg.mall.product.dao.*;
import com.jiangyg.mall.product.dto.*;
import com.jiangyg.mall.product.entity.*;
import com.jiangyg.mall.product.enums.Published;
import com.jiangyg.mall.product.enums.Searchable;
import com.jiangyg.mall.product.vo.SpuAttrVO;
import com.jiangyg.mall.product.vo.SpuInfoVO;
import com.jiangyg.mall.search.dto.SkuIndexDTO;
import com.jiangyg.mall.search.service.SkuSearchService;
import com.jiangyg.mall.ware.service.WareSkuService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.StopWatch;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class SpuServiceImpl extends BaseServiceImpl<SpuInfoDao, SpuInfo> implements SpuService {

    private final AttrDao attrDao;

    private final BrandDao brandDao;

    private final CategoryDao categoryDao;

    private final SpuImagesDao spuImagesDao;

    private final SpuInfoDescDao spuInfoDescDao;

    private final SpuAttrValueDao spuAttrValueDao;

    private final SkuInfoDao skuInfoDao;

    private final SkuImagesDao skuImagesDao;

    private final SkuSaleAttrValueDao skuSaleAttrValueDao;

    private final AttrgroupDao attrgroupDao;

    private final AttrAttrgroupDao attrAttrgroupDao;

//    @Reference
    @Autowired
    private WareSkuService wareSkuService;

//    @Reference
    @Autowired
    private SkuSearchService skuSearchService;

//    @Reference
    @Autowired
    private SpuBoundsService spuBoundsService;

//    @Reference
    @Autowired
    private SkuConcessionService skuConcessionService;

    /**
     * 事物管理器
     */
    private final DataSourceTransactionManager transactionManager;

    @Autowired
    public SpuServiceImpl(AttrDao attrDao, BrandDao brandDao,
                          CategoryDao categoryDao, SpuInfoDao baseMapper,
                          SkuInfoDao skuInfoDao, SpuImagesDao spuImagesDao,
                          SpuInfoDescDao spuInfoDescDao, SpuAttrValueDao spuAttrValueDao,
                          SkuImagesDao skuImagesDao, SkuSaleAttrValueDao skuSaleAttrValueDao,
                          AttrgroupDao attrgroupDao, AttrAttrgroupDao attrAttrgroupDao,
                          DataSourceTransactionManager transactionManager) {
        super(baseMapper);
        this.attrDao = attrDao;
        this.brandDao = brandDao;
        this.categoryDao = categoryDao;
        this.spuImagesDao = spuImagesDao;
        this.spuInfoDescDao = spuInfoDescDao;
        this.spuAttrValueDao = spuAttrValueDao;
        this.skuInfoDao = skuInfoDao;
        this.skuImagesDao = skuImagesDao;
        this.skuSaleAttrValueDao = skuSaleAttrValueDao;
        this.attrgroupDao = attrgroupDao;
        this.attrAttrgroupDao = attrAttrgroupDao;
        this.transactionManager = transactionManager;
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public Pagination<SpuInfoVO> selectPagination(Pagination<SpuInfoVO> pagination) throws Exception {
        // TODO 优化
        final SpuInfoDTO searchModel = pagination.toSearchModel(SpuInfoDTO.class);
        Pager<SpuInfoVO> pager = Pager.from(pagination);
        pager = this.baseMapper.selectPagination(pager, searchModel);
        return pager.toPagination(SpuInfoVO.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean save(SpuSaveDTO dto) throws Exception {
        // 1 保存spu基本信息
        Long spuId = this.saveSpuInfo(dto);
        // 2 保存spu的介绍图片
        this.saveSpuIndoDesc(spuId, dto);
        // 3 保存spu的图片集
        this.saveSpuImages(spuId, dto);
        // 4 保存规格参数
        this.saveSpuAttrValue(spuId, dto.getBaseAttrs());
        // 5 保存spu积分信息
        this.saveSpuBounds(spuId, dto);
        // 6 保存sku信息
        final List<SkuSaveDTO> skus = dto.getSkus();
        if (CollectionUtils.isNotEmpty(skus)) {
            for (SkuSaveDTO sku : skus) {
                // 6.1 保存sku基本信息
                Long skuId = this.saveSkuInfo(spuId, sku, dto);
                // 6.2 保存sku图片集
                this.saveSkuImages(skuId, sku);
                // 6.3 保存sku销售属性
                this.saveSkuSaleAttrs(skuId, sku);
                // 6.4 保存sku优惠信息
                this.saveSkuConcession(skuId, sku);
            }
        }
        return true;
    }

    /**
     * 功能描述：保存spu基本信息
     *
     * @param dto 参数
     * @return spuId
     */
    private Long saveSpuInfo(SpuSaveDTO dto) throws Exception {
        final SpuInfo info = new SpuInfo();
        BeanUtils.copyProperties(info, dto);
        info.setCreateTime(new Date());
        info.setUpdateTime(new Date());
        final int count = this.baseMapper.insert(info);
        if (count <= 0) {
            throw BusinessException.instance("mall.product.spu.save.error");
        }
        return info.getId();
    }

    /**
     * 功能描述：保存spu的介绍图片
     *
     * @param spuId spuId
     * @param dto   参数
     */
    private void saveSpuIndoDesc(Long spuId, SpuSaveDTO dto) {
        final List<String> introduce = dto.getIntroduce();
        final SpuInfoDesc spuInfoDesc = new SpuInfoDesc();
        spuInfoDesc.setSpuId(spuId);
        spuInfoDesc.setIntroduce(StringUtils.join(introduce, ","));
        this.spuInfoDescDao.insert(spuInfoDesc);
    }

    /**
     * 功能描述：保存spu的图片集
     *
     * @param spuId spuId
     * @param dto   参数
     */
    private void saveSpuImages(Long spuId, SpuSaveDTO dto) {
        final List<String> images = dto.getImages();
        for (int i = 0; i < images.size(); i++) {
            if (StringUtils.isBlank(images.get(i))) {
                continue;
            }
            final SpuImages spuImages = new SpuImages();
            spuImages.setUrl(images.get(i));
            spuImages.setSort(i + 1);
            spuImages.setSpuId(spuId);
            this.spuImagesDao.insert(spuImages);
        }
    }

    /**
     * 功能描述：保存规格参数
     *
     * @param spuId     spuId
     * @param baseAttrs 参数
     */
    private int saveSpuAttrValue(Long spuId, List<SpuAttrDTO> baseAttrs) {
        if (CollectionUtils.isEmpty(baseAttrs)) {
            return 0;
        }
        int index = 0;
        for (SpuAttrDTO attrDTO : baseAttrs) {
            final Attr attr = attrDao.selectById(attrDTO.getId());
            if (attr == null) {
                Logger.error(log, () -> String.format("保存SPU失败：规则参数[%s]不存在！", attrDTO.getId()));
                throw BusinessException.instance("mall.product.spu.save.notfind.attr");
            }
            ++index;
            final SpuAttrValue spuAttrValue = new SpuAttrValue();
            spuAttrValue.setSpuId(spuId);
            spuAttrValue.setAttrId(attr.getId());
            spuAttrValue.setAttrName(attr.getName());
            spuAttrValue.setAttrValue(attrDTO.getOptions());
            spuAttrValue.setAttrVisible(attrDTO.getVisible());
            spuAttrValue.setAttrSort(index);
            this.spuAttrValueDao.insert(spuAttrValue);
        }
        return index;
    }

    /**
     * 功能描述：保存规格参数
     *
     * @param spuId spuId
     * @param dto   参数
     */
    private void saveSpuBounds(Long spuId, SpuSaveDTO dto) throws Exception {
        final SpuBoundsDTO bounds = dto.getBounds();
        bounds.setSpuId(spuId);
        spuBoundsService.saveOrUpdate(bounds);
    }

    /**
     * 功能描述：保存sku信息
     *
     * @param spuId spuId
     * @param sku   sku
     * @param dto   dto
     * @return skuId
     */
    private Long saveSkuInfo(Long spuId, SkuSaveDTO sku, SpuSaveDTO dto) {
        // 默认图片
        String defaultImage = null;
        if (CollectionUtils.isNotEmpty(sku.getImages())) {
            final SkuImagesDTO images = sku.getImages().stream().filter(i -> i.getIsDefault() == 1).findFirst().orElse(null);
            defaultImage = images != null ? images.getUrl() : null;
        }
        // 保存SKU
        final SkuInfo skuInfo = new SkuInfo();
        skuInfo.setTitle(sku.getTitle());
        skuInfo.setName(sku.getName());
        skuInfo.setDesct(sku.getDesct());
        skuInfo.setSubtitle(sku.getSubtitle());
        skuInfo.setPrice(sku.getPrice());
        skuInfo.setDefaultImage(defaultImage);
        skuInfo.setSpuId(spuId);
        skuInfo.setCategoryId(dto.getCategoryId());
        skuInfo.setBrandId(dto.getBrandId());
        final int count = this.skuInfoDao.insert(skuInfo);
        if (count <= 0) {
            throw BusinessException.instance("mall.product.sku.save.error");
        }
        return skuInfo.getId();
    }

    /**
     * 功能描述：保存sku图片集
     *
     * @param skuId skuId
     * @param sku   sku信息
     */
    private void saveSkuImages(Long skuId, SkuSaveDTO sku) {
        final List<SkuImagesDTO> images = sku.getImages();
        if (CollectionUtils.isEmpty(images)) {
            return;
        }
        int index = 1;
        for (SkuImagesDTO image : images) {
            if (StringUtils.isBlank(image.getUrl())) {
                continue;
            }
            final SkuImages skuImages = new SkuImages();
            skuImages.setSkuId(skuId);
            skuImages.setSort(index);
            skuImages.setUrl(image.getUrl());
            skuImages.setIsDefault(image.getIsDefault());
            skuImagesDao.insert(skuImages);
            ++index;
        }
    }

    /**
     * 功能描述：保存sku销售属性
     *
     * @param skuId skuId
     * @param sku   sku信息
     */
    private void saveSkuSaleAttrs(Long skuId, SkuSaveDTO sku) {
        final List<SkuAttrDTO> attrs = sku.getAttrs();
        if (CollectionUtils.isEmpty(attrs)) {
            return;
        }
        int index = 1;
        for (SkuAttrDTO attrDTO : attrs) {
            final Attr attr = attrDao.selectById(attrDTO.getId());
            if (attr == null) {
                Logger.error(log, () -> String.format("保存SKU失败：规则参数[%s]不存在！", attrDTO.getId()));
                throw BusinessException.instance("mall.product.sku.save.notfind.attr");
            }
            final SkuSaleAttrValue skuSaleAttrValue = new SkuSaleAttrValue();
            skuSaleAttrValue.setSkuId(skuId);
            skuSaleAttrValue.setAttrId(attr.getId());
            skuSaleAttrValue.setAttrName(attr.getName());
            skuSaleAttrValue.setAttrValue(attrDTO.getOptions());
            skuSaleAttrValue.setAttrSort(index);
            this.skuSaleAttrValueDao.insert(skuSaleAttrValue);
            ++index;
        }
    }

    /**
     * 功能描述：保存sku优惠信息
     *
     * @param skuId skuId
     * @param sku   sku信息
     */
    private void saveSkuConcession(Long skuId, SkuSaveDTO sku) throws Exception {
        final SkuConcessionDTO concession = new SkuConcessionDTO();
        concession.setSkuId(skuId);
        BeanUtils.copyProperties(concession, sku);
        skuConcessionService.save(concession);
    }

    // 发布比较耗时，不使用事物
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public boolean published(Long spuId) throws Exception {
        StopWatch watch = new StopWatch(String.format("商品[%d]发布", spuId));
        // 1、根据 spuId 查询 SKU 待发布数据，并验证 SPUID 是否正确
        watch.start("查询SKU数据列表");
        final List<SkuIndexDTO> skuList = this.loadSkuPublishedData(spuId);
        watch.stop();
        // 2、组装 SKU 图片集
        watch.start("查询SKU图片集");
        this.assembleSkuImages(skuList);
        watch.stop();
        // 3、组装 SKU 是否有库存
        watch.start("查询SKU库存");
        this.assembleSkuInStock(skuList);
        watch.stop();
        // 4、组装分类信息
        watch.start("查询SKU分类信息");
        this.assembleSkuCategory(skuList);
        watch.stop();
        // 5、组装品牌信息
        watch.start("查询SKU品牌信息");
        this.assembleSkuBrand(skuList);
        watch.stop();
        // 6. 组装检索属性
        watch.start("查询SKU属性信息");
        this.assembleSpuBaseAttrAndGroup(spuId, skuList);
        watch.stop();
        // 7. 组装销售属性
        watch.start("查询SKU销售属性信息");
        this.assembleSkuSaleAttr(skuList);
        watch.stop();
        // 8. 组装SPU描述信息
        watch.start("查询SPU描述信息");
        this.assembleSpuInfo(spuId, skuList);
        watch.stop();
        // 9. 远程调用检索服务，生成索引
        watch.start("创建SKU索引");
        if (!skuSearchService.save(skuList)) {
            throw BusinessException.instance("mall.product.spu.published.saveindex.error");
        }
        watch.stop();
        // 10. 更新发布状态
        watch.start("更新商品发布状态");
        this.updatePublished(spuId, Published.UP);
        watch.stop();
        Logger.info(log, watch::prettyPrint);
        return true;
    }

    /**
     * 功能描述：更新商品的发布状态
     *
     * @param spuId     商品ID
     * @param published 发布状态
     */
    private void updatePublished(Long spuId, Published published) {
        TransactionStatus status = null;
        try {
            // 1. 获取事务定义，设置事务隔离级别（新事务）
            final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
            def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            status = transactionManager.getTransaction(def);
            // 2. 更新 SPU 的发布状态
            {
                final UpdateWrapper<SpuInfo> wrapper = new UpdateWrapper<>();
                wrapper.eq(ColumnConstant.ID, spuId);
                final SpuInfo spuInfo = new SpuInfo();
                spuInfo.setPublished(published.code);
                this.baseMapper.update(spuInfo, wrapper);
            }
            // 3. 更新 SPU 下 SKU 的发布状态
            final UpdateWrapper<SkuInfo> wrapper = new UpdateWrapper<>();
            wrapper.eq(ColumnConstant.SPU_ID, spuId);
            final SkuInfo skuInfo = new SkuInfo();
            skuInfo.setPublished(published.code);
            this.skuInfoDao.update(skuInfo, wrapper);
            // 4. 提交事物
            transactionManager.commit(status);
        } catch (Exception ex) {
            Logger.error(log, () -> String.format("[发布商品]-更新商品[%d]的SPU和SKU发布状态失败", spuId), ex);
            if (status != null) {
                transactionManager.rollback(status);
            }
            throw ex;
        }
    }

    /**
     * 功能描述：根据 spuId 查询 SKU 待发布数据
     *
     * @param spuId spuId
     * @return 结果
     */
    private List<SkuIndexDTO> loadSkuPublishedData(Long spuId) {
        // 根据SPUID查询所有 SKU 信息
        final QueryWrapper<SkuInfo> wrapper = new QueryWrapper<>();
        wrapper.eq(ColumnConstant.SPU_ID, spuId);
        final List<SkuInfo> skuInfos = this.skuInfoDao.selectList(wrapper);
        // SKU信息不能为空
        if (CollectionUtils.isEmpty(skuInfos)) {
            throw BusinessException.instance("mall.product.spu.published.withoutskuinfo.error");
        }
        final List<SkuIndexDTO> skuList = new ArrayList<>();
        // 组装SKU基本信息
        for (SkuInfo sku : skuInfos) {
            SkuIndexDTO index = new SkuIndexDTO();
            index.setId(sku.getId());
            index.setTitle(sku.getTitle());
            index.setSubtitle(sku.getSubtitle());
            index.setPrice(sku.getPrice().doubleValue());
            index.setSales(sku.getSales());
            index.setBrand(new SkuIndexDTO.Brand(sku.getBrandId()));
            index.setCategory(new SkuIndexDTO.Category(sku.getCategoryId()));
            index.setSpu(new SkuIndexDTO.Spu(sku.getSpuId()));
            skuList.add(index);
        }
        return skuList;
    }

    /**
     * 功能描述：组装 SKU 图片集
     *
     * @param skuList 数据集
     */
    private void assembleSkuImages(List<SkuIndexDTO> skuList) {
        // 获取 skuId 集合
        final List<Long> skuIds = skuList.stream().map(SkuIndexDTO::getId).collect(Collectors.toList());
        // 查询 sku 所有的图片信息
        final QueryWrapper<SkuImages> wrapper = new QueryWrapper<>();
        wrapper.in(ColumnConstant.SKU_ID, skuIds);
        wrapper.orderByAsc(ColumnConstant.SKU_ID, ColumnConstant.SORT);
        final List<SkuImages> skuImages = this.skuImagesDao.selectList(wrapper);
        // 反写 sku 的图片集
        skuList.forEach(sku -> {
            final List<SkuIndexDTO.Image> images = skuImages.stream()
                    .filter(img -> img.getSkuId().equals(sku.getId()))
                    .sorted(Comparator.comparingInt(SkuImages::getSort))
                    .map(img -> new SkuIndexDTO.Image(img.getUrl(), img.getIsDefault().equals(1)))
                    .collect(Collectors.toList());
            sku.setImages(images);
        });
    }

    /**
     * 功能描述：组装 SKU 是否有库存
     *
     * @param skuList 数据集
     */
    private void assembleSkuInStock(List<SkuIndexDTO> skuList) {
        // 获取 skuId 集合
        final List<Long> skuIds = skuList.stream().map(SkuIndexDTO::getId).collect(Collectors.toList());
        // 查询 sku 所有的库存信息
        final Map<Long, Boolean> stockMap = wareSkuService.inStock(skuIds);
        // 反写 sku 的存库信息
        skuList.forEach(sku -> {
            Boolean inStock = MapUtils.getBoolean(stockMap, sku.getId());
            sku.setInStock(inStock != null && inStock);
        });
    }

    /**
     * 功能描述：组装 SKU 分类信息
     *
     * @param skuList 数据集
     */
    private void assembleSkuCategory(List<SkuIndexDTO> skuList) {
        // 获取 categoryId 集合
        final List<Long> categoryIds = skuList.stream().map(d -> d.getCategory().getId()).distinct().collect(Collectors.toList());
        // 查询分类信息
        final QueryWrapper<Category> wrapper = new QueryWrapper<>();
        wrapper.in(ColumnConstant.ID, categoryIds);
        final List<Category> categories = this.categoryDao.selectList(wrapper);
        // 反写 sku 的分类名称
        skuList.forEach(sku -> {
            SkuIndexDTO.Category skuCate = sku.getCategory();
            if (skuCate == null) {
                return;
            }
            categories.stream()
                    .filter(c -> c.getId().equals(skuCate.getId()))
                    .findFirst()
                    .ifPresent(c -> skuCate.setName(c.getName()));
        });
    }

    /**
     * 功能描述：组装 SKU 品牌信息
     *
     * @param skuList 数据集
     */
    private void assembleSkuBrand(List<SkuIndexDTO> skuList) {
        // 获取 brandId 集合
        final List<Long> brandIds = skuList.stream().map(s -> s.getBrand().getId()).distinct().collect(Collectors.toList());
        // 查询分类信息
        final QueryWrapper<Brand> wrapper = new QueryWrapper<>();
        wrapper.in(ColumnConstant.ID, brandIds);
        final List<Brand> brands = this.brandDao.selectList(wrapper);
        // 反写 sku 的品牌信息
        skuList.forEach(sku -> {
            SkuIndexDTO.Brand brand = sku.getBrand();
            if (brand == null) {
                return;
            }
            brands.stream()
                    .filter(b -> b.getId().equals(brand.getId()))
                    .findFirst()
                    .ifPresent(b -> {
                        brand.setName(b.getName());
                        brand.setLogo(b.getLogo());
                    });
        });
    }

    /**
     * 功能描述：组装 SPU 描述信息
     *
     * @param spuId   spuId
     * @param skuList 数据集
     */
    private void assembleSpuInfo(Long spuId, List<SkuIndexDTO> skuList) {
        final QueryWrapper<SpuInfoDesc> wrapper = new QueryWrapper<>();
        wrapper.eq(ColumnConstant.SPU_ID, spuId);
        List<SpuInfoDesc> descs = this.spuInfoDescDao.selectList(wrapper);
        if (CollectionUtils.isEmpty(descs)) {
            return;
        }
        skuList.forEach(sku -> sku.getSpu().setIntroduce(descs.get(0).getIntroduce()));
    }

    /**
     * 功能描述：组装 SKU 属性和分组信息
     *
     * @param spuId   spuId
     * @param skuList 数据集
     */
    private void assembleSpuBaseAttrAndGroup(Long spuId, List<SkuIndexDTO> skuList) {
        // 查询SPU所有属性信息
        final QueryWrapper<SpuAttrValue> wrapper = new QueryWrapper<>();
        wrapper.eq(ColumnConstant.SPU_ID, spuId);
        final List<SpuAttrValue> attrValueList = this.spuAttrValueDao.selectList(wrapper);
        if (CollectionUtils.isEmpty(attrValueList)) {
            return;
        }
        // 获取所有的属性ID
        final List<Long> attrIds = attrValueList.stream().map(SpuAttrValue::getAttrId).distinct().collect(Collectors.toList());
        // 组装 SKU 属性信息
        this.assembleSpuBaseAttr(attrIds, attrValueList, skuList);
        // 组装 SKU 分组信息
        assembleSpuBaseAttrGroup(attrIds, skuList);

    }

    /**
     * 功能描述：组装 SKU 分组信息
     *
     * @param attrIds 属性ID集合
     * @param skuList 数据集
     */
    private void assembleSpuBaseAttrGroup(List<Long> attrIds, List<SkuIndexDTO> skuList) {
        // 根据属性ID集合找到分组关联关系
        final List<AttrAttrgroup> attrAttrgroups;
        {
            final QueryWrapper<AttrAttrgroup> wrapper = new QueryWrapper<>();
            wrapper.in(ColumnConstant.ATTR_ID, attrIds);
            attrAttrgroups = this.attrAttrgroupDao.selectList(wrapper);
        }
        // 查询分组信息
        final List<Attrgroup> attrgroups;
        {
            final List<Long> attrgroupIds = attrAttrgroups.stream().map(AttrAttrgroup::getAttrgroupId).distinct().collect(Collectors.toList());
            final QueryWrapper<Attrgroup> wrapper = new QueryWrapper<>();
            wrapper.in(ColumnConstant.ID, attrgroupIds);
            attrgroups = this.attrgroupDao.selectList(wrapper);
        }
        // 分析分组和属性关系信息
        final List<SkuIndexDTO.BaseAttrGroup> baseAttrGroups = attrgroups.stream()
                .map(attrgroup -> {
                    final SkuIndexDTO.BaseAttrGroup group = new SkuIndexDTO.BaseAttrGroup();
                    group.setId(attrgroup.getId());
                    group.setName(attrgroup.getName());
                    group.setIcon(attrgroup.getIcon());
                    final List<Long> attrIdsForGroup = attrAttrgroups.stream()
                            .filter(aag -> aag.getAttrgroupId().equals(attrgroup.getId()))
                            .map(AttrAttrgroup::getAttrId)
                            .collect(Collectors.toList());
                    group.setAttrIds(attrIdsForGroup);
                    return group;
                }).collect(Collectors.toList());
        // 反写 sku 的分组信息
        skuList.forEach(sku -> sku.setBaseAttrGroups(baseAttrGroups));
    }

    /**
     * 功能描述：组装 SKU 属性信息
     *
     * @param attrIds       属性ID集合
     * @param attrValueList 属性值
     * @param skuList       数据集
     */
    private void assembleSpuBaseAttr(List<Long> attrIds, List<SpuAttrValue> attrValueList, List<SkuIndexDTO> skuList) {
        // 查询SPU属性设置检索的集合
        final List<Long> searchableAttrIds;
        {
            final QueryWrapper<Attr> wrapper = new QueryWrapper<>();
            wrapper.in(ColumnConstant.ID, attrIds);
            wrapper.eq(ColumnConstant.SEARCHABLE, Searchable.Y.code);
            final List<Attr> attrs = this.attrDao.selectList(wrapper);
            if (CollectionUtils.isEmpty(attrs)) {
                return;
            }
            searchableAttrIds = attrs.stream().map(Attr::getId).collect(Collectors.toList());
        }
        // 生成的 SPU 属性
        List<SkuIndexDTO.BaseAttr> attrs = attrValueList.stream()
                .map(attr -> {
                    final Long attrId = attr.getAttrId();
                    final boolean searchable = searchableAttrIds.contains(attrId);
                    return new SkuIndexDTO.BaseAttr(attrId, attr.getAttrName(), attr.getAttrValue(), searchable);
                })
                .collect(Collectors.toList());
        // 反写 sku 的属性
        skuList.forEach(sku -> sku.setBaseAttrs(attrs));
    }

    /**
     * 功能描述：组装 SKU 销售属性信息
     *
     * @param skuList 数据集
     */
    private void assembleSkuSaleAttr(List<SkuIndexDTO> skuList) {
        // 查询 sku 所有的销售属性信息
        final List<SkuSaleAttrValue> attrValueList;
        {
            final List<Long> skuIds = skuList.stream().map(SkuIndexDTO::getId).collect(Collectors.toList());
            final QueryWrapper<SkuSaleAttrValue> wrapper = new QueryWrapper<>();
            wrapper.in(ColumnConstant.SKU_ID, skuIds);
            attrValueList = this.skuSaleAttrValueDao.selectList(wrapper);
            if (CollectionUtils.isEmpty(attrValueList)) {
                return;
            }
        }
        // 反写 sku 的可检索的属性
        skuList.forEach(sku -> {
            final List<SkuIndexDTO.SaleAttr> saleAttrs = attrValueList.stream()
                    .filter(attr -> attr.getSkuId().equals(sku.getId()))
                    .map(attr -> new SkuIndexDTO.SaleAttr(attr.getAttrId(), attr.getAttrName(), attr.getAttrValue(), attr.getSkuId()))
                    .collect(Collectors.toList());
            sku.setSaleAttrs(saleAttrs);
        });
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public List<SpuAttrVO> listAttr(Long spuId) {
        // 查询 SPU 属性
        final QueryWrapper<SpuAttrValue> wrapper = new QueryWrapper<>();
        wrapper.eq(ColumnConstant.SPU_ID, spuId);
        final List<SpuAttrValue> attrValueList = this.spuAttrValueDao.selectList(wrapper);
        // 转换结果
        return attrValueList.stream().map(av -> new SpuAttrVO(String.valueOf(av.getAttrId()), av.getAttrVisible(), av.getAttrValue())).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveBaseAttrs(Long spuId, List<SpuAttrDTO> baseAttrs) {
        // 删除规格参数
        final UpdateWrapper<SpuAttrValue> wrapper = new UpdateWrapper<>();
        wrapper.eq(ColumnConstant.SPU_ID, spuId);
        this.spuAttrValueDao.delete(wrapper);
        // 保存规格参数
        return this.saveSpuAttrValue(spuId, baseAttrs) > 0;
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public List<SpuInfoVO> selectListByIds(List<Long> ids) throws Exception {
        if (CollectionUtils.isEmpty(ids)) {
            return Collections.emptyList();
        }
        return BeanUtils.transformList(this.baseMapper.selectBatchIds(ids), SpuInfoVO.class);
    }

}
